package fem2;

import inf.jlinalg.IMatrix;
import inf.jlinalg.IMatrixReadOnly;
import inf.jlinalg.IVector;
import inf.jlinalg.IVectorReadOnly;
import fem2.enu.IntegrationType;
import fem2.enu.MassMatrixType;
import fem2.enu.MeshPartType;

/**
 * The abstract <code>Element</code> class. This element is targeted to serve
 * for both linear & nonlinear formulation & dynamics & inelastic (3/26/12)
 * 
 * @author hbui
 */
public abstract class Element extends ModelPart implements ElementPart {

	private int p = 1;
	private IntegrationType integrationType = IntegrationType.NONE;

	/**
	 * Constructs the element.
	 * 
	 * @param mp
	 *            the mesh part associated with the element
	 */
	public Element(MeshPart mp) {
		super(mp);
		getMeshPart().setElement(this);
	}

	/**
	 * get the ID number of a field.
	 * 
	 * @param idx
	 *            the index of the field
	 * @return the ID of the fields
	 */
	public abstract int getFieldId(int idx);

	/**
	 * Computes mass matrix
	 * 
	 * @return me
	 */
	public IMatrixReadOnly computeMe(IMatrix me, MassMatrixType massType) {
		throw new NotImplementedError();
	}

	/**
	 * Computes the element tangent stiffness matrix.
	 * 
	 * @return the read-only element stiffness matrix
	 */
	public abstract IMatrixReadOnly computeKte(IMatrix kte);

	/**
	 * Computes the internal force vector.
	 * 
	 * @return the read-only internal force vector
	 */
	public abstract IVectorReadOnly computeRie(IVector rie);

	/**
	 * Computes the resistant force vector.
	 * 
	 * @return the read-only resistant force vector
	 */
	public abstract IVectorReadOnly computeRes(IVector res);

	/**
	 * 
	 * @return integration order of associated element
	 */
	public int getIntegrationOrder() {
		return p;
	}

	/**
	 * set integration order for this element.
	 * 
	 * @param p
	 */
	public void setIntegrationOrder(int p) {
		this.p = p;
	}

	/**
	 * 
	 * @return the integration type of associated element
	 */
	public IntegrationType getIntegrationType() {
		return this.integrationType;
	}

	/**
	 * set integration type on the element
	 * 
	 * @param integrationType
	 */
	public void setIntegrationType(IntegrationType integrationType) {
		this.integrationType = integrationType;

		/*
		 * set the order of integration on this element
		 */
		MeshPartType mpType = getMeshPart().getType();
		setIntegrationOrder(IntegrationManager.getInstance().getDefaultIntegrationOrder(mpType));
	}

	/**
	 * get the element type (triangle, quadrilaterial) associated with element
	 * 
	 * @return
	 */
	public String getElemType() {
		MeshPartType mpType = getMeshPart().getType();
		if (mpType == MeshPartType.TRI3 || mpType == MeshPartType.TRI6) {
			return "Triangle";
		} else if (mpType == MeshPartType.QUAD4 || mpType == MeshPartType.QUAD8) {
			return "Quadrilateral";
		} else if (mpType == MeshPartType.TET4 || mpType == MeshPartType.TET10) {
			return "Tetrahedra";
		} else if (mpType == MeshPartType.HEX8 || mpType == MeshPartType.HEX20) {
			return "Hexahedra";
		} else if (mpType == MeshPartType.LINE2 || mpType == MeshPartType.LINE3) {
			return "Linear";
		} else {
			throw new Error("not supported mesh part type");
		}
	}

	/**
	 * 
	 * @return number of Gauss points associated with element (full integration)
	 */
	public abstract int getNumIntegrationPoints();

	/**
	 * @param k
	 * @return natural coordinates of k-th Gauss point for full integration
	 *         scheme
	 */
	public abstract double[] getIntegrationPoint(int k);

	/**
	 * 
	 * @param k
	 * @return weight of k-th Gauss point for full integration scheme
	 */
	public abstract double getIntegrationWeight(int k);

	/**
	 * 
	 * @param k
	 *            index of Gauss point
	 * @return physical coordinates of k-th Gauss point
	 */
	public final double[] getGaussPointCoordinate(int k) {
		return getMeshPart().XAt(getIntegrationPoint(k));
	}

}
